home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / science / ack3d.zip / ACK3D.DOC next >
Text File  |  1994-01-09  |  64KB  |  1,958 lines

  1.  
  2.  
  3.                Animation Construction Kit 3D
  4.                  ACK-3D
  5.                    Lary Myers
  6.  
  7.  
  8. Contents:
  9.   1 ........... Purpose
  10.   2 ........... Using the ACK engine (DISCLAIMER)
  11.   3 ........... Hardware Requirements
  12.   4 ........... Development Environment
  13.   5 ........... Multi-User
  14.   6 ........... The Interface Structure
  15.   7 ........... Important fields in the Interface Structure
  16.   8 ........... Array Formats in the Interface Structure
  17.   9 ........... Initializing the ACK-3D engine
  18.  10 ........... ACK-3D Bitmaps
  19.  11 ........... Loading Bitmaps
  20.  12 ........... Creating Objects
  21.  13 ........... Using Overlays
  22.  14 ........... Moving around in ACK-3D
  23.  15 ........... Moving objects
  24.  16 ........... Normal and Secret Doors
  25.  17 ........... Sound and ACK-3D
  26.  18 ........... Function summary
  27.  19 ........... Where to Begin (EXAMPLES)
  28.  20 ........... Closing Comments
  29.  
  30.  
  31.  
  32.  
  33. (1) Purpose:
  34.  
  35.     ACK-3D began as an experiment to emulate the 3D effects seen in games like
  36. Wolfenstein-3D and Ken's Labyrinth and to make the technique available to the
  37. general public. So far things have worked out very well and in some areas have
  38. even exceeded the before mentioned games. With this latest installment ACK-3D
  39. gives the developer an easy to use library that can be linked into thier own
  40. applications without having to delve into the inner workings of the ray-casting
  41. engine. My hope is that others will use the ACK-3D engine to produce a new
  42. generation of 3D games that all of us can enjoy.
  43.  
  44.     Please forgive any glaring mistakes in this documentation. I find that I can
  45. write code all day long without getting weary, but as soon as I have to write
  46. documentation, forget it, too much like work! I hope the text below gives you
  47. enough information to use the engine.
  48.  
  49.  
  50.  
  51. (2) Using the ACK-3D engine: (DISCLAIMER)
  52.  
  53.     The ACK-3D engine is being released as "PublicWare" and can be freely used
  54. in any private or commercial programs. The only restriction I would impose is
  55. that anything developed with the engine have its own disclaimer about liability
  56. and that under no circumstances will the author (me) be liable for any damages
  57. of any kind that may result from the use of the engine. "Basically the engine
  58. is being released "AS-IS" so it's left up to the developer to use it in a
  59. professional manner".
  60.  
  61.     If you develope a wonderful new game using the engine, I'd appreciate a
  62. mention in the documentation or credits screen, but only if you feel the ACK
  63. engine helped the game. Thanks.
  64.  
  65.  
  66. (3) Hardware Requirements:
  67.  
  68.     The current version of ACK-3D requires a minimum of a 386 33Mhz machine and
  69. will not run on a 286 or below because of 386 code being used. A coprocessor
  70. is NOT required or needed for the engine itself. Any other hardware requirements
  71. are left up to the applications that use the engine (ie. mouse, VGA, etc).
  72. A VGA is only required if the ACK display routines are used (which is up to the
  73. application). If the application chooses to use another display mode then it
  74. should issue the appropriate hardware requirements.
  75.  
  76.  
  77.  
  78.  
  79. (4) Development Environment:
  80.  
  81.     ACK-3D was developed using BorlandC Version 3.1 in compact model. It is
  82. setup to also use large model (both may be supplied). The assembly routines
  83. were compiled using the Microsoft Assembler (MASM) version 5.00A and should
  84. work with later versions of MASM. The library was created using the Microsoft
  85. Library Manager (LIB) version 3.02 and should also work with later versions
  86. of LIB.
  87.  
  88.     All of the ACK code is written in either normal C or assembler.
  89.  
  90.     Compiler switches used with Borland;
  91.  
  92.     -ml = Large model
  93.  
  94.     -c  = Compile only, don't link
  95.  
  96.     -3  = Use 386 instructions
  97.  
  98.     -O2 = Compile for size
  99.  
  100.     -G  = Compile for speed
  101.  
  102.     -I  = specifies the include path for header files
  103.  
  104.     -o  = specifies the object path and file name
  105.  
  106.  
  107.     Assembler switches used with MASM;
  108.  
  109.     /Ml    = Case sensitive for all symbols and labels
  110.  
  111.     /B63    = Size of buffer during assembly (does not affect OBJ file)
  112.  
  113.     /D_ML
  114.     /D_MC    = Assembly directive for large (_ML) or compact (_MC) model.
  115.           Used by ET.MAC for various macros.
  116.  
  117.  
  118.     Directory structure;
  119.  
  120.     I use the following subdirectories for the ACK engine and demo. This
  121.     is provided for your information. It is what the .MAK files are setup for.
  122.  
  123.     Engine source;
  124.  
  125.         \borlandc\ack3d\eng        <- .C .H .MAK files
  126.  
  127.         \borlandc\ack3d\eng\cmobj    <- Compact model .OBJ and .LIB files
  128.  
  129.         \borlandc\ack3d\eng\lmobj    <- Large model .OBJ and .LIB files
  130.  
  131.  
  132.     Demo source;
  133.  
  134.         \borlandc\ack3d\demo    <- .C .H .BAT .MAK .EXE .DAT files
  135.  
  136.         \borlandc\ack3d\demo\bitmaps <- .BBM and .LBM files
  137.  
  138.         \borlandc\ack3d\demo\sound    <- .VOC .CMF .PWM files
  139.  
  140.  
  141.  
  142.  
  143.  
  144. (5) Multi-User:
  145.  
  146.     ACK-3D uses an interface structure to communicate between the application
  147. and the engine. This structure (which will be explained later) contains the
  148. pertinent data that the engine needs to draw the current Point of View (POV).
  149. It is possible to do some fancy things with this approach. Here are some of
  150. the things I've tried;
  151.  
  152.     1. Displaying more than one view at the same time such as directly in
  153.        front (the normal view) and directly behind (essentially eyes in the
  154.        back of your head!).
  155.  
  156.     2. Displaying more than one player on the map is also possible but has
  157.        some restrictions that may make it unusable. These restrictions may
  158.        be lifted in the near future.
  159.  
  160.  
  161.     Some discussion has been going on for a network version of ACK-3D. While
  162. this is not built into the engine for this version, it does seem to be a
  163. straightforward progression from this point. Future versions of the engine
  164. may indeed support multiple players logging into a 3D world and interacting
  165. with each other in real-time!
  166.  
  167.  
  168. (6) The Interface Structure:
  169.  
  170.     ACK-3D uses a structure from the application to get the necessary
  171. information for building a complete POV. It is the applications responsibility
  172. to correctly create and setup this structure before the ACK engine is called.
  173. Limited bounds checking is in the engine to prevent degradation of speed during
  174. the build process. UNPREDICTABLE RESULTS MAY OCCUR WITH INVALID DATA!
  175.  
  176.     The structure is defined in the header file ACK3D.H and can be either in
  177. the data segment of the calling application or allocated in memory whichever
  178. the application desires. Within the structure are variables for things like the
  179. current location and angle of the player, the actual map arrays for the walls
  180. and objects, pointers to the screen buffer, overlay buffer and background
  181. buffer, as well as dimensions of the viewport to build the walls into. The
  182. term "build" is being used here because the ACK engine will not display to the
  183. screen unless told to do so in a separate function. This way the application
  184. is free to display the current POV anyway it wants to, or it can use the
  185. supplied function to display if it doesn't. This offers maximum flexibility in
  186. how the engine is used.
  187.  
  188.  
  189.  
  190. (7) Important fields in the Interface Structure:
  191.  
  192.     Before calling the ACK engine to initialize, some very important items
  193. need to be filled into the interface structure. These are described below;
  194.  
  195.     WinStartX        <- The leftmost pixel coordinate of the viewport
  196.     WinEndX        <- The rightmost pixel coordinate of the viewport
  197.     WinStartY        <- The upper pixel coordinate of the viewport
  198.     WinEndY        <- The lower pixel coordinate of the viewport
  199.  
  200.     These four fields will allow the ACK engine to fill in some other fields
  201. within the interface structure, such as WinWidth, WinHeight, etc.
  202.  
  203.     Before calling the function to construct the background, the following
  204. fields must be filled in;
  205.  
  206.     TopColor        <- Color value of the ceiling
  207.     BottomColor        <- Color value of the floor
  208.     LightFlag        <- Whether light shading is on or off
  209.  
  210.     Before calling the function to actually build the POV, the following fields
  211. must be filled in;
  212.  
  213.     xPlayer        <- X coordinate for the POV
  214.     yPlayer        <- Y coordinate for the POV
  215.     PlayerAngle        <- POV angle in ACK units
  216.  
  217.  
  218.     The ACK engine relies on the POV fields above to build the current scene.
  219. By changing these values it's possible to build a variety of scenes one after
  220. the other. For example, changing the PlayerAngle to be 180 degrees from the
  221. current angle and then calling the build routine, will create a scene that is
  222. directly behind the current POV.
  223.  
  224.  
  225.  
  226. (8) Array Formats in the Interface Structure:
  227.  
  228.     Several arrays are used to build the 3D POV scene. ACK-3D uses a map layed
  229. out in squares like graph paper to determine what the POV sees at any given
  230. time. This map is a 2 dimensional array of 64 columns by 64 rows. When the
  231. initialize routine reads in this map, it processes the data and builds the
  232. arrays in the interface structure. These arrays are xGrid and yGrid which are
  233. used for drawing the walls. The layout of these arrays is different from the
  234. map file and is described below;
  235.  
  236.  
  237.         1,1,1,1,1,1,1,1    Example map of size 8x8, the real map
  238.         1,0,0,0,0,0,0,1        would be the same except 64x64
  239.         1,0,0,0,0,0,0,1
  240.         1,0,1,1,1,0,0,1
  241.         1,0,1,0,1,0,0,1
  242.         1,0,1,1,1,0,0,1
  243.         1,0,0,0,0,0,0,1
  244.         1,1,1,1,1,1,1,1
  245.  
  246.  
  247.  
  248.     This is an example of a map where 0 is a blank square and non-zero values
  249. represent wall bitmap numbers (from 1 to 255). The ACK engine requires this
  250. same information broken down into walls that fall on X planes and walls that
  251. fall on Y planes, so the array above is changed into;
  252.  
  253.         1,1,1,1,1,1,1,1,1
  254.         1,0,0,0,0,0,0,1,1    This would be a map of X walls, note the extra
  255.         1,0,0,0,0,0,0,1,1    column on the right side of the map.
  256.         1,0,1,1,1,0,0,1,1
  257.         1,0,1,0,1,0,0,1,1
  258.         1,0,1,1,1,0,0,1,1
  259.         1,0,0,0,0,0,0,1,1
  260.         1,1,1,1,1,1,1,1,1
  261.  
  262.  
  263.  
  264.         1,1,1,1,1,1,1,1
  265.         1,0,0,0,0,0,0,1    This would be a map of Y walls, note the extra
  266.         1,0,0,0,0,0,0,1    row on the bottom of the map.
  267.         1,0,1,1,1,0,0,1
  268.         1,0,1,0,1,0,0,1
  269.         1,0,1,1,1,0,0,1
  270.         1,0,0,0,0,0,0,1
  271.         1,1,1,1,1,1,1,1
  272.         1,1,1,1,1,1,1,1
  273.  
  274.  
  275.     This is basically what becomes of the original map in order for the engine
  276. to see walls in either the X or Y planes. What this means is that anytime the
  277. map needs to be looked at, the following applies;
  278.  
  279.  
  280.     int    MapPosn;        /* Location to look at in map */
  281.  
  282.  
  283.     xGrid[MapPosn];            /* This would be the left X walls */
  284.     xGrid[MapPosn + 1];        /* This would be the right X wall */
  285.  
  286.     yGrid[MapPosn];            /* This would be the top Y wall */
  287.     yGrid[MapPosn + GRID_WIDTH];    /* This would be the bottom wall */
  288.  
  289.  
  290.     If all four of these locations is 0 then there is nothing at this square in
  291. the map. What does this mean? One thing that would be possible (but not part of
  292. the ACK engine), is that walls would not have to be cubes, but could be single
  293. wall panels (might look alittle funny from the side since they wouldn't have
  294. any width to them). It could also mean that different bitmaps could be used
  295. for various sides of the cube.
  296.  
  297.  
  298.     The next two arrays are bMaps and oMaps which, for now, are simple arrays
  299. that point to the wall and object bitmaps respectively. Each bitmap takes
  300. 4096 bytes of memory for the 64x64 size bitmap. BUT, the important point is
  301. that ACK reads these bitmaps in normal row order, where there is a row of
  302. color bytes one after the other, like so;
  303.  
  304.         1,2,3,...        An arbitrary bitmap of 64x64
  305.         1,1,1,...
  306.         4,2,4,...
  307.         ....
  308.  
  309.     This bitmap is then "rotated" 90 degrees so it is in column order, which
  310. makes it easier to use when drawing the bitmap, thus the above becomes;
  311.  
  312.         1,1,4,....        Bitmap rotated 90 degrees
  313.         2,1,2,....
  314.         3,1,4,....
  315.  
  316.   -----------------------------------------
  317.     Another important array in the interface structure is the PalTable[] array
  318. which controls how the ACK engine will perform light shading. The array contains
  319. 16 ranges of 256 colors each which are used to substitute for the actual colors
  320. of the bitmap based on the distance away from the POV.
  321.  
  322.     The distance to the wall or object is first divided by 64 to get the zone
  323. to use for shading (zones greater than 15 are set at 15). This zone of 256 is
  324. then used as a lookup table indexed by the color of the bitmap. What all this
  325. allows is a gradual darkening of walls and objects as they become farther away
  326. from the POV while still allowing some colors to be used as constant lighting
  327. colors (they will never darken with distance).
  328.  
  329.  
  330.   -----------------------------------------
  331.  
  332.     Now we come to doors. Within the interface structure there is a sub-
  333. structure labeled Door[MAX_DOORS]. This structure array holds the current
  334. status of all doors that may be in the process of opening or closing. This
  335. array is NOT all the doors in the map, only those that are in motion. If the
  336. ColOffset field of the array is non-zero the door is active and either opening
  337. or closing. When an application makes a call to AckCheckDoorOpen() the engine
  338. will determine if the POV is close enough to a door and initialize it in the
  339. Door[] array. After that, every call to AckBuildView() will cause the door to
  340. be updated until it goes fully open then fully closed. During this time the
  341. application is free to alter the fields in the Door[] array (BEWARE that mPos
  342. and mPos1 are map positions and should NOT be altered during this time). If,
  343. for example, the application wants a door to remain open for a long period of
  344. time, it can check the value of ColOffset to see where a door currently is. If
  345. the value is less than 64 the door is partially open (or partially closed).
  346. Once the value becomes greater than 64 the door is no longer visible and is
  347. fully open. By keeping the value greater than 64 (setting the Speed field to
  348. zero) the door will no longer be moved. Another way might be to set the field
  349. ColOffset to zero and not allow the door to be seen anymore. This method would
  350. be the best if the application desires the door to remain permanently open.
  351. Here is a breakdown of the DOORS structure;
  352.  
  353.  
  354.     mPos        <- Map position for one side of cube
  355.  
  356.     mPos1        <- Map position for other side of cube
  357.  
  358.     mCode        <- Bitmap code that represents the door
  359.  
  360.     mCode1        <- Bitmap code for door on other side
  361.  
  362.     ColOffset   <- Current column offset of door (see above)
  363.  
  364.     Speed        <- Speed is added to ColOffset during open/close
  365.  
  366.     Type        <- Type of door (ie DOOR_XCODE or DOOR_YCODE)
  367.  
  368.     Flags        <- Current door action (DOOR_OPENING or DOOR_CLOSING)
  369.  
  370.     The current version of the ACK engine uses the following wall bitmap values
  371. for doors;
  372.  
  373.     60        <- Door lies on the X plane (vertical)
  374.  
  375.     61        <- Side panel that appears on both sides of door
  376.  
  377.     62        <- Door lies on the Y plane (horizontal)
  378.  
  379.  
  380.   -----------------------------------------
  381.  
  382.     Okay, now let's look at the OBJECT sub-structure. Again, this is an array
  383. stored within the interface structure and accessable to the application as well
  384. as the ACK engine. Every object in the map is represented by an entry in this
  385. array. The AckInitialize() function automatically fills in the initial X and Y
  386. coordinates of the object when it finds one in the map file. The rest of the
  387. object data must be filled in by the application before the objects can be
  388. used. The section below on Creating Objects describes what fields should be
  389. setup by the application.
  390.  
  391.     Some of the other fields in the OBJECT array are described below;
  392.  
  393.     Active        <- 0 or 1 to indicate the object should be considered during
  394.                the AckCheckObjectMovement() function.
  395.  
  396.     bmNum        <- Holds from 1 to MAX_VIEW bitmap indices to display for
  397.                the object. If more than 1 bitmap the object will either
  398.                animate in place or display multiple views as the POV
  399.                walks around the object.
  400.  
  401.     Sides        <- Calculated by AckCreateObject() function dependent on
  402.                the number of bitmaps in bmNum.
  403.  
  404.     Dir        <- Which direction the object will move.
  405.  
  406.     Flags        <- This field can contain OF_PASSABLE which allows the POV
  407.                to walk right through the object. (Good for overhead
  408.                lights and such). It can also contain OF_ANIMATE, which
  409.                is used by the function AckCheckObjectMovement() to
  410.                cycle between multiple bitmap images for the object.
  411.  
  412.     CurNum        <- Current index (base 0) of the bitmap to display out of
  413.                bmNum. If the object only has 1 bitmap then CurNum will
  414.                always be 0.
  415.  
  416.     MaxNum        <- Total number of bitmaps (base 0) contained in bmNum. If
  417.                the object only has 1 bitmap then MaxNum will be 1.
  418.  
  419.     Speed        <- The speed at which the object will move. Normal values
  420.                are from 2 to 40, higher than this causes alot of
  421.                jmuping and may cause the object to pass through walls.
  422.  
  423.     VidRow        <- Reserved.
  424.  
  425.     x and y        <- Map coordinates of the object, from 0 to 4095.
  426.  
  427.     mPos        <- The actual map grid location of the object. If x and y
  428.                are changed by the application then mPos should also
  429.                be updated by using the equation;
  430.  
  431.             mPos = (y & 0xFFC0) + (x >> 6);
  432.  
  433.  
  434.     The Dir field is a left-over from the older version of the engine, where
  435. it was used for crude 8 direction movement, or rotating in place. With this
  436. latest version, the OF_ANIMATE flag is used to indicate cycling through the
  437. bitmaps for the object and the Dir field is ignored. The application can
  438. still use this field for storing an angle of movement for use with the
  439. function AckMoveObjectPOV().
  440.  
  441.  
  442.  
  443.  
  444. (9) Initializing the ACK-3D engine:
  445.  
  446.     Before anything can be done with the ACK engine, it must be initialized.
  447. This is done by creating the interface structure and passing it into the
  448. AckInitialize() function. The following example shows one method to accomplish
  449. this;
  450.  
  451.  
  452.     ACKENG    ae;        /* Interface structure in global memory */
  453.  
  454.     int main()
  455.     {
  456.     int    result;
  457.  
  458.  
  459.     ae.WinStartY = 0;        /* Setup viewport coordinates */
  460.     ae.WinEndY     = 104;
  461.     ae.WinStartX = 0;
  462.     ae.WinEndX     = 319;
  463.  
  464.     result = AckInitialize(&ae);       /* Initialize the engine */
  465.  
  466.     if (result)
  467.     {
  468.     printf("Error initializing - Code: %d\n",result);
  469.     exit(1);
  470.     }
  471.  
  472.     result = AckReadMapFile(&ae,"DEMOMAP.L01"); /* Read map file */
  473.  
  474.     if (result)
  475.     {
  476.     printf("Error reading map - Code: %d\n",result);
  477.     exit(1);
  478.     }
  479.  
  480.     }
  481.                 Example 1
  482.  
  483.  
  484.     The function AckInitialize() will return an error code if there was a
  485. problem initializing the engine or a zero if successful. At this point the
  486. ACKENG interface structure is initialized, the file "TRIG.DAT" has been
  487. read into the various tables that ACK-3D requires.
  488.  
  489.     No bitmaps have been dealt with yet so there is still more to do before
  490. actually drawing the first POV.
  491.  
  492.     No objects have been dealt with yet. The application must handle these
  493. either on its own or by using one of the supplied ACK-3D functions.
  494.  
  495.  
  496.  
  497.  
  498. (10) ACK-3D Bitmaps:
  499.  
  500.     All bitmaps used by ACK-3D are 64 pixels wide by 64 pixels tall and are
  501. stored in normal line by line fashion (ie. One row of 64 pixels then another
  502. row, and so forth). Each byte represents one pixel and may have a color from
  503. 0 to 255. The engine itself requires the bitmaps to be in raw image form,
  504. however the function AckLoadBitmap() will accept either raw IMG format or
  505. Deluxe Paint II brush files (.BBM extensions).
  506.  
  507.  
  508. (11) Loading Bitmaps:
  509.  
  510.     ACK-3D provides several functions to load in bitmaps. These functions don't
  511. have to be used and are only provided for convienence to the application. They
  512. are as follows;
  513.  
  514.     NOTE: If the application chooses to load it's own bitmaps it MUST be sure
  515.       to rotate the bitmaps 90 degrees into the column/row order needed
  516.       by the engine. Also, the function below uses Extended memory (XMS)
  517.       if available which the application may or may not decide to support.
  518.  
  519.  
  520.     int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,
  521.               char *bmFileName);
  522.  
  523.     where:
  524.         ae         <- Interface structure
  525.         BitmapNumber <- A value from 1 to 255
  526.         BitmapType   <- Either TYPE_WALL or TYPE_OBJECT
  527.         bmFileName   <- Name of bitmap file to read
  528.  
  529.     This is the general purpose bitmap load routine. It will read either
  530.     raw image files (.IMG) or Deluxe Paint brush files (.BBM) and place the
  531.     bitmap into the appropriate bitmap array (bMaps for walls and oMaps for
  532.     objects).
  533.  
  534.  
  535.     int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
  536.  
  537.     where:
  538.         ae         <- Interface structure
  539.         WallNumber   <- Value from 1 to 255
  540.         bmFileName   <- Name of bitmap file to read
  541.  
  542.     This routine simply calls AckLoadBitmap() with TYPE_WALL set.
  543.  
  544.  
  545.     int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
  546.  
  547.     where:
  548.         ae         <- Interface structure
  549.         BmpNumber    <- Value from 1 to 255
  550.         bmFileName   <- Name of bitmap file to read
  551.  
  552.     This routine simply calls AckLoadBitmap() with TYPE_OBJECT set.
  553.  
  554.  
  555.  
  556.  
  557. (12) Creating Objects:
  558.  
  559.     Objects in ACK-3D can be either stationary or movable depending on the needs
  560. of the application. Both types of object are handled exactly the same, the
  561. stationary ones just never change location. Objects can share bitmaps if needed
  562. and can have more than one bitmap to accomplish different tasks. If the object
  563. has multiple bitmaps it can either stay in one spot and display the bitmaps
  564. in sequence (this animates the object) or can use the mulitple bitmaps to show
  565. different views of the object when the POV is walking around it.
  566.  
  567.     The sequence for creating an object is as follows;
  568.  
  569.     1) Load any bitmaps associated with the object.
  570.  
  571.     2) Specify the object speed and any flags such as OF_PASSABLE or OF_ANIMATE.
  572.  
  573.     3) Call the routine AckCreateObject() to fill-in other information in the
  574.        object structure.
  575.  
  576.     Example;
  577.  
  578.     int main()
  579.     {
  580.  
  581.     /* Initialization done per Example 1 above */
  582.  
  583.     AckLoadObject(&ae,1,"object1.bbm");        /* Load in a bitmap for object */
  584.  
  585.     ae.ObjList[1].Flags |= OF_ANIMATE;        /* Stay in place and animate     */
  586.     ae.ObjList[1].Speed = 1;            /* Speed is non-zero to activate */
  587.     AckCreateObject(&ae,1,1,nums);
  588.  
  589.  
  590.     }
  591.  
  592.                 Example 2
  593.  
  594.  
  595.     The example above uses an array of unsigned chars called nums[] to specify
  596. the different views of the object. In this case there is only one so the number
  597. of views is also passed as 1. There can be as many as MAX_VIEWS (ack3d.h)
  598. different bitmaps assigned to one object for use as animation, etc.
  599.  
  600.  
  601.  
  602. (13) Using Overlays:
  603.  
  604.     If the application so desires, an overlay screen can be used with ACK-3D.
  605. This overlay will only be effective if the AckDisplayScreen() function is used,
  606. unless the application manipulates the overlay on its own.
  607.  
  608.     What an overlay does is allow a full screen picture to be used which may
  609. overlay some of the area where walls are displayed. There could be ancient
  610. pillars, or merely a sign that says "Demo in progress" or whatever, the whole
  611. point to the overlay is to provide a means of displaying graphics over the top
  612. of the viewport when displayed on the screen.
  613.  
  614.     The overlay is read in like a normal bitmap, except it is full-screen in
  615. size (320x200). It is then compiled into drawing commands that are placed in
  616. the interface structure pointed to by OverlayBuffer. Everytime the function
  617. AckDisplayScreen() is called, this pointer is used to draw the overlay on top
  618. of the last POV that was built.
  619.  
  620.     Overlays are optional and do not need to be used unless desired by the
  621. application. For those who wish to process the compiled overlay themselves,
  622. here is the format for the OverlayBuffer;
  623.  
  624.  
  625.     Length    2 bytes        <- Length of data below (does not include offset)
  626.     Offset    2 bytes        <- Offset into screen to show data
  627.     Data    n bytes        <- Actual data of size Length
  628.  
  629.     Length,Offset,Data combinations will continue until a Length of zero
  630. is reached, meaning no more data.
  631.  
  632.     The function AckDrawOverlay(ScreenBuffer,OverlayBuffer) can be used to
  633. place the overlay on top of any walls that were drawn. This function processes
  634. the overlay commands described above and draws into the screen buffer. The
  635. application can then do any additional drawing it desires before actually
  636. displaying to the video.
  637.  
  638.  
  639. (14) Moving around in ACK-3D:
  640.  
  641.     Once things are initialized and the current POV is built and displayed, it
  642. becomes time to move around in the map. This is accomplished by using the
  643. function:
  644.  
  645.     int AckMovePOV(ACKENG *ae,int Angle,int Amount);
  646.  
  647.     where:
  648.         ae         <- Interface structure
  649.         Angle         <- Direction to move
  650.         Amount         <- Amount to move
  651.  
  652.  
  653.     In its simplest form the function can just be called with the current angle
  654. the POV is facing and some amount to move, such as;
  655.  
  656.     AckMovePOV(&ae,ae.PlayerAngle,16);
  657.  
  658.     But it can also be used to backup, with the following;
  659.  
  660.     NewAngle = ae.PlayerAngle + INT_ANGLE_180;
  661.  
  662.     if (NewAngle >= INT_ANGLE_360)
  663.         NewAngle -= INT_ANGLE_360;
  664.  
  665.     AckMovePOV(&ae,NewAngle,16);
  666.  
  667.     This function does the necessary collision detection and returns 0 if the
  668. POV actually moved, in which case the values ae.xPlayer and ae.yPlayer have
  669. been updated with the new coordinates of the POV.
  670.  
  671.     If the application wishes to do the moving itself, it can make a call to
  672. the function AckCheckHit() which returns 0 if no walls are in the way. Any
  673. collisions with objects will not be returned from AckCheckHit.
  674.  
  675.  
  676.  
  677. (15) Moving Objects:
  678.  
  679.     ACK-3D contains two functions for handling object animation and movement.
  680. Animation is performed by switching the displayed bitmap for the object
  681. whenever the function is called. The application is responsible for setting
  682. up the object structure to provide the engine with the necessary information
  683. to animate or move the object.
  684.  
  685.     void AckCheckObjectMovement(ACKENG *ae);
  686.  
  687.     This function will check all active objects and determine if any need
  688. to have thier bitmaps changed for animation. The application can perform this
  689. check itself if so desired. This function is provided for convienence.
  690.  
  691.  
  692.     int AckMoveObjectPOV(ACKENG *ae,int ObjIndex,int Angle,int Amount);
  693.  
  694.     This function actually moves the object at the specified Angle for the
  695. specified Amount. The return value can be processed by the application to
  696. determine if the object has struck a wall, another object, or the player.
  697.  
  698.  
  699.  
  700. (16) Normal and Secret Doors:
  701.  
  702.     Once ACK-3D reads in the map file, it is processed for a variety of things,
  703. one of these being where doors will appear in the map. The application should
  704. establish where doors will appear within the map file BEFORE calling the
  705. function AckInitialize(). Doors are treated as special walls in the ACK engine
  706. and will not automatically be checked to see if they should open. This is up
  707. to the application and can be accomplished with the function;
  708.  
  709.  
  710.     void AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
  711.  
  712.     where:
  713.         xPlayer        <- Current x coordinate of the POV
  714.         yPlayer        <- Current y coordinate of the POV
  715.         PlayerAngle        <- Current angle the POV is facing
  716.         ae            <- Pointer to the interface structure
  717.  
  718.     What this function does is determine if the POV is close enough to either
  719. a normal or secret door and set the appropriate information to begin opening
  720. the door. This information is kept in the Door[] array of the interface
  721. structure and is then used by the ACK engine during subsequent builds. After
  722. the door has been triggered to open, the process is automatic (unless the
  723. application manipulates the Door array, which it can if need be) until the
  724. door goes back to fully closed.
  725.  
  726.  
  727. (17) Sound and ACK-3D:
  728.  
  729.     The ACK engine is being released with a public domain program from Mystic
  730. software that allows playing of SoundBlaster .CMF and .VOC files. In addition
  731. the files with .PWM extensions are capable of playing through the PC speaker.
  732.  
  733.     Since the sound features are public domain there are some limitations that
  734. are imposed by Mystic Software. The first is that a Terminate and Stay Resident
  735. program (TSR) must be loaded before using the sound routines. The program is
  736. included with the ACK demo and is called WORXLITE.EXE. This TSR need be loaded
  737. only once when first running the demo program.
  738.  
  739.     The second limitation is how the sound is used. Sound files consume alot
  740. of memory so don't try to load several of them or large ones, they won't fit.
  741. There also appears to be some problems with the sound routines that need to
  742. be watched out for. On some machines it appears that playing background music
  743. with the .CMF files will occasionally lock up the machine and require a hard
  744. reboot to clear it. Playing the sound effect (.VOC files) alone seems to work
  745. fine. The other problem is that .VOC files cannot be loaded after background
  746. music is started playing, this forces all VOC files to be loaded up front which
  747. contributes to the memory overhead mentioned earlier.
  748.  
  749.     But, the sound routines are provided to allow you to experiment with them
  750. in the 3D engine without having to write your own. The full fledged WORX
  751. library can be purchased from Mystic Software if you wish to use it in a full
  752. fledged shareware or commercial application.
  753.  
  754.  
  755.  
  756. (18) Function Summary:
  757.  
  758.     Here is a list of the available ACK-3D functions and what they do.
  759.  
  760. ------------------------------------------------------------------------------
  761. int AckInitialize(ACKENG *ae);
  762.     where:
  763.     ae        <- Pointer to interface structure
  764.  
  765.     Purpose:
  766.     Initializes the various arrays used by the ACK engine.
  767.     Reads and processes the file "TRIG.DAT".
  768.     Reads and processes the Map file supplied in the call.
  769.     Allocates 64000 bytes for the ScreenBuffer.
  770.     Calculates viewport items based on initial dimensions setup by the
  771.         application.
  772.  
  773.     Returns:
  774.     0 if successful
  775.       One of the error codes listed in ACK3D.H
  776.  
  777.     Notes:
  778.     This function must be called before any other ACK function.
  779.  
  780.  
  781.  
  782. ------------------------------------------------------------------------------
  783. int AckReadMapFile(ACKENG *ae,char *MapFileName);
  784.     where:
  785.     ae        <- Pointer to interface structure
  786.     MapFileName <- Name of map/object file to read
  787.  
  788.     Purpose:
  789.     Reads and processes the ACK binary map file.
  790.  
  791.     Returns:
  792.     0 if successful
  793.       One of the error codes listed in ACK3D.H
  794.  
  795.     Notes:
  796.     Upon return the xGrid and yGrid arrays will be filled in with the
  797.     wall bitmap numbers. The application is then free to change these if it
  798.     wishes some walls to be different.
  799.  
  800.  
  801. ------------------------------------------------------------------------------
  802. int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,char *bmFileName);
  803.     where:
  804.     ae        <- Pointer to interface structure
  805.     BitmapNumber <- Index number into bitmap array
  806.     BitmapType  <- Either TYPE_WALL or TYPE_OBJECT
  807.     bmFileName  <- Name of bitmap file to read
  808.  
  809.     Purpose:
  810.     Reads in either raw (IMG) or DPII (BBM) bitmap file and processes it
  811.     into the form that ACK-3D requires.
  812.     Allocates the 4K of memory for the bitmap and stores the pointer in
  813.     either bMaps[] or oMaps[] array based on BitmapType.
  814.  
  815.     Returns:
  816.     0 if successful
  817.       One of the error codes listed in ACK3D.H
  818.  
  819.     Notes:
  820.     This function now uses XMS (Extended Memory) when possible to load
  821.     bitmaps.
  822.  
  823.  
  824. ------------------------------------------------------------------------------
  825. int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
  826.     where:
  827.     ae        <- Pointer to interface structure
  828.     WallNumber  <- Index number into bitmap array
  829.     bmFileName  <- Name of bitmap file to read
  830.  
  831.     Purpose:
  832.     Calls AckLoadBitmap() function with TYPE_WALL BitmapType set.
  833.  
  834.     Returns:
  835.     0 if successful
  836.       One of the error codes listed in ACK3D.H
  837.  
  838.     Notes:
  839.     This function now uses XMS (Extended Memory) when possible to load
  840.     bitmaps.
  841.  
  842.  
  843. ------------------------------------------------------------------------------
  844. int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
  845.     where:
  846.     ae        <- Pointer to interface structure
  847.     BmpNumber   <- Index number into bitmap array
  848.     bmFileName  <- Name of bitmap file to read
  849.  
  850.     Purpose:
  851.     Calls AckLoadBitmap() function with TYPE_OBJECT BitmapType set.
  852.  
  853.     Returns:
  854.     0 if successful
  855.       One of the error codes listed in ACK3D.H
  856.  
  857.     Notes:
  858.     This function now uses XMS (Extended Memory) when possible to load
  859.     bitmaps.
  860.  
  861.  
  862.  
  863. ------------------------------------------------------------------------------
  864. int AckCreateObject(ACKENG *ae,int ObjNumber,int NumBitmaps,UCHAR *bmNums);
  865.     where:
  866.     ae        <- Pointer to interface structure
  867.     ObjNumber   <- Index number into ObjList array (Different than bitmap
  868.                             number!).
  869.     NumBitmaps  <- Number of bitmap indexes contained in bmNums.
  870.     bmNums        <- List of bitmap numbers associated with this object.
  871.  
  872.     Purpose:
  873.     Sets up ObjList structure with information regarding the object.
  874.     Calculates the number of sides to the object if multiple bitmaps are
  875.     specified.
  876.  
  877.     Returns:
  878.     0 if successful
  879.       One of the error codes listed in ACK3D.H
  880.  
  881.     Notes:
  882.     None
  883.  
  884.  
  885. ------------------------------------------------------------------------------
  886. int AckCreateOverlay(ACKENG *ae, UCHAR far *OverlayScreen);
  887.     where:
  888.     ae        <- Pointer to interface structure
  889.     OverlayScreen <- Pointer to 64k screen image to use.
  890.  
  891.     Purpose:
  892.     Determines which part of the screen is within the viewport and compiles
  893.     this area for use by the AckDisplayScreen() function. The compiled
  894.     commands are placed in the pointer OverlayBuffer in the interface
  895.     structure.
  896.  
  897.     Returns:
  898.     0 if successful
  899.       One of the error codes listed in ACK3D.H
  900.  
  901.     Notes:
  902.     This function process the overlay screen passed and builds a compiled
  903.     overlay buffer (which is allocated) and returned in ae->OverlayBuffer.
  904.  
  905.  
  906. ------------------------------------------------------------------------------
  907. int AckBuildBackground(ACKENG *ae);
  908.     where:
  909.     ae        <- Pointer to interface structure
  910.  
  911.     Purpose:
  912.     Builds a static floor and ceiling background image based on the values
  913.     of TopColor, BottomColor and LightFlag in the interface structure. The
  914.     resulting image is pointed to by BkgdBuffer in the interface structure.
  915.  
  916.     Returns:
  917.     0 always
  918.  
  919.     Notes:
  920.     This function builds a psuedo-shaded ceiling and floor picture that is
  921.     used for the background of the screen. The application can override this
  922.     and have its own background if desired.
  923.  
  924.  
  925. ------------------------------------------------------------------------------
  926. int AckBuildView(ACKENG *ae);
  927.     where:
  928.     ae        <- Pointer to interface structure
  929.  
  930.     Purpose:
  931.     Constructs the current POV and places the result into ScreenBuffer
  932.     pointer in the interface structure. No displaying is done to the
  933.     screen at this time, nor has the optional overlay buffer been processed
  934.     by this function.
  935.  
  936.     Returns:
  937.     0 always
  938.  
  939.     Notes:
  940.     The best place to put this function is in the applications main loop
  941.     so it is repeatedly called whenever the POV moves or objects move or
  942.     animate.
  943.  
  944.  
  945. ------------------------------------------------------------------------------
  946. void AckDrawOverlay(UCHAR far *Screen,UCHAR far *Overlay);
  947.     where:
  948.     Screen        <- Buffer to draw overlay buffer into
  949.     Overlay        <- Pointer to compiled overlay image
  950.  
  951.     Purpose:
  952.     Processes the compiled overlay image and draws into the screen buffer.
  953.  
  954.     Notes:
  955.     This function should be called before AckDisplayScreen (or the
  956.     applications own display function) if an overlay is used.
  957.  
  958.  
  959.  
  960. ------------------------------------------------------------------------------
  961. int AckDisplayScreen(ACKENG *ae);
  962.     where:
  963.     ae        <- Pointer to interface structure
  964.  
  965.     Purpose:
  966.     Display the last built POV onto the screen in normal VGA mode 13h.
  967.  
  968.  
  969.     Returns:
  970.     0 always
  971.  
  972.     Notes:
  973.     If the application wishes to use an overlay it must call the function,
  974.     AckDrawOverlay() before calling AckDisplayScreen().
  975.  
  976.  
  977. ------------------------------------------------------------------------------
  978. void AckCheckObjectMovement(ACKENG *ae);
  979.     where:
  980.     ae        <- Pointer to interface structure
  981.  
  982.     Purpose:
  983.     Scans the ObjList sub-structure of the interface structure to determine
  984.     if any objects need to be updated. This function should be called
  985.     before the AckBuildView() function.
  986.  
  987.     Returns:
  988.     Nothing
  989.  
  990.     Notes:
  991.     This routine is mainly good for animating objects that have multiple
  992.     bitmaps. The application should devise its own movement algorithms and
  993.     call AckMoveObjectPOV() to carry them out.
  994.  
  995.  
  996. ------------------------------------------------------------------------------
  997. int AckMovePOV(ACKENG *ae,int Angle,int Amount);
  998.     where:
  999.     ae        <- Pointer to interface structure
  1000.     Angle        <- Angle to move POV
  1001.     Amount        <- Amount to move POV
  1002.  
  1003.     Purpose:
  1004.     Determines if the move is valid and then sets the new coordinates into
  1005.     xPlayer and yPlayer of the interface structure.
  1006.  
  1007.     Returns:
  1008.     0 if successful
  1009.     1 if X wall was hit
  1010.     2 if Y wall was hit
  1011.     3 if an object was hit
  1012.  
  1013.     Notes:
  1014.     None
  1015.  
  1016.  
  1017.  
  1018.  
  1019. ------------------------------------------------------------------------------
  1020. int AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
  1021.     where:
  1022.     xPlayer        <- Current x coordinate of the POV
  1023.     yPlayer        <- Current y coordinate of the POV
  1024.     PlayerAngle <- Current angle POV is facing
  1025.     ae        <- Pointer to interface structure
  1026.  
  1027.     Purpose:
  1028.     Determines if the POV is close enough to trigger a door open. If so,
  1029.     the door is placed in the Door sub-structure of the interface structure
  1030.     and the opening process is begun. Subsequent calls to AckBuildView()
  1031.     will automatically continue the open and close process.
  1032.  
  1033.     Returns:
  1034.     0 if no door was opened
  1035.     1 if X door was opened
  1036.     2 if Y door was opened
  1037.     3 if X secret door was opened
  1038.     4 if Y secret door was opened
  1039.  
  1040.     Notes:
  1041.     None
  1042.  
  1043.  
  1044.  
  1045. ------------------------------------------------------------------------------
  1046. int AckCheckHit(int xPlayer,int yPlayer,int ViewAngle,ACKENG *ae);
  1047.     where:
  1048.     xPlayer        <- Current x coordinate of the POV
  1049.     yPlayer        <- Current y coordinate of the POV
  1050.     ViewAngle   <- Current angle POV is facing
  1051.     ae        <- Pointer to interface structure
  1052.  
  1053.     Purpose:
  1054.     Allows the application to determine if an obstacle is close to the POV.
  1055.  
  1056.     Returns:
  1057.     0 if nothing nearby
  1058.     1 if X wall is close
  1059.     2 if Y wall is close
  1060.  
  1061.     Notes:
  1062.     This function is called by AckMovePOV() and checks for collisions with
  1063.     walls (not objects).
  1064.  
  1065. ------------------------------------------------------------------------------
  1066. int AckGetObjectHit(void);
  1067.  
  1068.     Purpose:
  1069.     Allows the application to determine the object the POV last came in
  1070.     contact with.
  1071.  
  1072.     Returns:
  1073.     Returns the object index of the last object the POV hit.
  1074.  
  1075.     Notes:
  1076.     None
  1077.  
  1078.  
  1079.  
  1080. ------------------------------------------------------------------------------
  1081. int AckDeleteObject(ACKENG *ae,int ObjectIndex);
  1082.     where:
  1083.     ae        <- Pointer to interface structure
  1084.     ObjectIndex <- Index number of object to delete
  1085.  
  1086.     Purpose:
  1087.     Essentially sets the objects Active flag to 0 so the object is no longer
  1088.     checked by the engine. Good idea to call this function for future
  1089.     versions which may need to do more processing.
  1090.  
  1091.     Returns:
  1092.     -1 if object already inactive
  1093.      0 if object was deleted from map
  1094.  
  1095.     Notes:
  1096.     None
  1097.  
  1098.  
  1099. ------------------------------------------------------------------------------
  1100. void AckSetVGAmode(void);
  1101.  
  1102.     Purpose:
  1103.     Places the screen into standard 320x200 VGA mode 13h.
  1104.  
  1105.     Returns:
  1106.     Nothing
  1107.  
  1108.     Notes:
  1109.     This is a utility function. The application can set the screen to
  1110.     graphics using its own routines if it so desires, the ACK engine does not
  1111.     depend on mode 13h unless the AckDisplayScreen() function is called.
  1112.  
  1113.  
  1114. ------------------------------------------------------------------------------
  1115. int AckLoadAndSetPalette(char *FileName);
  1116.     where:
  1117.     FileName    <- Name of palette file to load
  1118.  
  1119.     Purpose:
  1120.     Reads the specified palette file then sets the palette of the VGA.
  1121.  
  1122.     Returns:
  1123.     0 if successful
  1124.       One of the error codes listed in ACK3D.H
  1125.  
  1126.     Notes:
  1127.     This is a utility function to read a palette file (768 bytes) and
  1128.     set the video palette to the contents of the file. The application can
  1129.     use it's own function if desired or can also use AckSetPalette() with
  1130.     a buffer if needed.
  1131.  
  1132.  
  1133. ------------------------------------------------------------------------------
  1134. void AckSetPalette(UCHAR far *PalBuffer);
  1135.     where:
  1136.     PalBuffer   <- 768 byte buffer containing palette information
  1137.  
  1138.     Purpose:
  1139.     Sets the contents of the PalBuffer into the video palette.
  1140.  
  1141.     Returns:
  1142.     Nothing
  1143.  
  1144.     Notes:
  1145.     Use this function to set a palette that has already been read into
  1146.     a buffer. Use AckLoadAndSetPalette() to read AND set a palette from a
  1147.     file. This is a utility function.
  1148.  
  1149.  
  1150. ------------------------------------------------------------------------------
  1151. void AckFadeIn(int Begin,int Count,UCHAR far *Palette);
  1152.     where:
  1153.     Begin        <- Starting color index to fade in
  1154.     Count        <- Number of sequential color indexes to fade
  1155.     Palette        <- Buffer containing palette information to use
  1156.  
  1157.     Purpose:
  1158.     Slowly fades the screen from black to the contents of the Palette
  1159.     buffer passed.
  1160.  
  1161.     Notes:
  1162.     This is a utility function.
  1163.  
  1164.  
  1165. ------------------------------------------------------------------------------
  1166. void AckFadeOut(int Begin,int Count);
  1167.     where:
  1168.     Begin        <- Starting color index to fade in
  1169.     Count        <- Number of sequential color indexes to fade
  1170.  
  1171.     Purpose:
  1172.     This function slowly fades the screen from the current palette to black.
  1173.  
  1174.     Notes:
  1175.     This is a utility function.
  1176.  
  1177.  
  1178. ------------------------------------------------------------------------------
  1179. void AckSetTextmode(void);
  1180.  
  1181.     Purpose:
  1182.     Places the screen into normal 80x25 text color mode 3.
  1183.  
  1184.     Returns:
  1185.     Nothing
  1186.  
  1187.     Notes:
  1188.     This is a utility function.
  1189.  
  1190.  
  1191. ------------------------------------------------------------------------------
  1192. UCHAR far *AckReadiff(char *FileName);
  1193.     where:
  1194.     FileName        <- Name of .LBM or .BBM file to read
  1195.  
  1196.     Purpose:
  1197.     Reads in a Deluxe Paint picture (.LBM) or brush (.BBM) file, allocates
  1198.     a buffer for the images and returns the buffer pointer to the caller.
  1199.     The image in the buffer will contain 4 bytes at the beginning with the
  1200.     width and height of the image in integer format.
  1201.     This function is provided if the application wishes to read its own
  1202.     images and is also needed if the overlay image is Deluxe Paint format.
  1203.  
  1204.     Returns:
  1205.     Pointer to buffer if successful
  1206.     NULL if an error reading the image
  1207.  
  1208.     Notes:
  1209.     None
  1210.  
  1211.  
  1212. ------------------------------------------------------------------------------
  1213. int AckWrapUp(ACKENG *ae);
  1214.     where:
  1215.     ae        <- Pointer to interface structure
  1216.  
  1217.     Purpose:
  1218.     Frees up memory buffers used by the ACK engine.
  1219.  
  1220.     Returns:
  1221.     0 always
  1222.  
  1223.     Notes:
  1224.     This function MUST be called before exiting the application so that
  1225.     XMS memory is returned to the system.
  1226.  
  1227.  
  1228. ------------------------------------------------------------------------------
  1229. int AckSoundInitialize(int DefaultSoundDevice);
  1230.     where:
  1231.     DefaultSoundDevice  <- One of the DEV_ values in ACKSND.H
  1232.  
  1233.     Purpose:
  1234.     Starts up WORX routines and determines hardware
  1235.  
  1236.     Returns:
  1237.     0 if okay
  1238.     -1 if error initializing
  1239.  
  1240.     Notes:
  1241.     This function MUST be called before the other sound routines are used.
  1242.     Calling with DEV_NOSOUND will force all sound off.
  1243.     Calling with DEV_PCSPEAKER will force sound through the speaker even
  1244.         if a sound card is present.
  1245.  
  1246.  
  1247. ------------------------------------------------------------------------------
  1248. int AckPlayBackground(char *MusicFileName);
  1249.     where:
  1250.     MusicFileName <- Name of .CMF file to begin playing in background.
  1251.  
  1252.     Purpose:
  1253.     Continuously plays the music file in the background.
  1254.  
  1255.     Returns:
  1256.     0 if okay
  1257.     -1 if error loading file
  1258.  
  1259.     Notes:
  1260.     See text in section Sound and ACK-3D for limitations with music.
  1261.  
  1262.  
  1263. ------------------------------------------------------------------------------
  1264. int AckLoadSound(int VocIndex,char *VocFileName);
  1265.     where:
  1266.     VocIndex    <- One of the SOUND_ defines in ACKSND.H
  1267.     VocFileName <- Name of .VOC file to load.
  1268.  
  1269.     Purpose:
  1270.     Loads up a sound file for later playing.
  1271.  
  1272.     Returns:
  1273.     0 if okay
  1274.     -1 if error loading sound
  1275.  
  1276.     Notes:
  1277.     This function will load the .VOC file if a SoundBlaster or Adlib card
  1278.     is present. If the PC speaker is specified then the routine will look for
  1279.     .PWM files (created with the VOC2PWM.EXE program). The application should
  1280.     always call with the .VOC extent.
  1281.  
  1282.  
  1283. ------------------------------------------------------------------------------
  1284. void AckPlaySound(int SoundIndex);
  1285.     where:
  1286.     SoundIndex  <- One of SOUND_ indexes from ACKSND.H
  1287.  
  1288.     Purpose:
  1289.     Plays the specified sound effect (.VOC file)
  1290.  
  1291.     Returns:
  1292.     Nothing
  1293.  
  1294.     Notes:
  1295.     Sound file must have been previously loaded with AckLoadSound().
  1296.  
  1297.  
  1298. ------------------------------------------------------------------------------
  1299. void AckStopBackground(void);
  1300.     where:
  1301.     Nothing
  1302.  
  1303.     Purpose:
  1304.     Stops the current background music file (.CMF)
  1305.  
  1306.     Returns:
  1307.     Nothing
  1308.  
  1309.     Notes:
  1310.     None
  1311.  
  1312.  
  1313. ------------------------------------------------------------------------------
  1314. void AckSoundShutdown(void);
  1315.     where:
  1316.     Nothing
  1317.  
  1318.     Purpose:
  1319.     Closes down the WORX routines, freeing memory, etc.
  1320.  
  1321.     Returns:
  1322.     Nothing
  1323.  
  1324.     Notes:
  1325.     This routine MUST be called before exiting the application if sound is
  1326.     being used in the ACK engine.
  1327.  
  1328.  
  1329.  
  1330.  
  1331.  
  1332. (19) Where to Begin: (EXAMPLES)
  1333.  
  1334.     Okay, now that we've covered some of the details, let's look at how an
  1335. application begins to use the ACK engine. The first thing to do is to make
  1336. some decisions, such as (and these may not be related to each other);
  1337.  
  1338.     1. Will the interface structure be in global memory, or allocated memory?
  1339.  
  1340.     2. What size of viewport will be used?
  1341.  
  1342.     3. Will light shading be used? Will the ceiling and floor be shaded?
  1343.  
  1344.     4. Are objects going to be manipulated by the ACK engine, or the app?
  1345.  
  1346.     5. Will there be an overlay screen?
  1347.  
  1348.  
  1349.     Why is question 1 important? The interface structure is not small, just to
  1350. begin with there are four 8k arrays within it to hold the map data, as well as
  1351. object and door arrays, so if you plan on having alot of global data in the
  1352. application then the interface structure should be allocated from the far heap.
  1353. It doesn't really matter to the engine since a pointer to the structure is
  1354. passed in the library calls as far data.
  1355.  
  1356.     Let's say for our example that we'll allocate the structure and hold it in
  1357. a global data pointer within the application. We could begin with something
  1358. like;
  1359.  
  1360. #include "ack3d.h"
  1361.  
  1362.     ACKENG        *ae;    /* This is our global pointer */
  1363.  
  1364. /* This could be a routine that the application uses to initialize things */
  1365. int AppInitialize(void)
  1366. {
  1367.     int        result = 0;
  1368.  
  1369. ae = malloc(sizeof(ACKENG));    /* We first get memory for the structure */
  1370.  
  1371. if (ae == NULL)            /* Whoops, we didn't get the memory */
  1372.     return(-1);            /* So return an error */
  1373.  
  1374. memset(ae,0,sizeof(ACKENG));    /* Now we clear out the entire structure */
  1375.  
  1376. /* Perform other initialization here */
  1377.  
  1378. return(result);
  1379. }
  1380.  
  1381.                 Snippet 1
  1382.  
  1383.  
  1384.     This code snippet does nothing more than allocate and clear the interface
  1385. structure that will be used by the application and the ACK engine. Now we need
  1386. to setup the size of the viewport and initialize the engine;
  1387.  
  1388. #include "ack3d.h"
  1389.  
  1390. #define VIEW_X        80        /* Size of the 3D viewport */
  1391. #define VIEW_X1        240
  1392. #define VIEW_Y        40
  1393. #define VIEW_Y1        160
  1394.  
  1395.  
  1396.     char        *MapFileName = "LEVEL1.MAP";
  1397.  
  1398.  
  1399. int AppSetupEngine(void)
  1400. {
  1401.     int        result;
  1402.  
  1403. ae->WinStartX = VIEW_X;
  1404. ae->WinStartY = VIEW_Y;                /* Plug in the size we want */
  1405. ae->WinEndX   = VIEW_X1;            /* for our viewport        */
  1406. ae->WinEndY   = VIEW_Y1;
  1407.  
  1408. result = AckInitialize(ae);            /* Then initialize the engine! */
  1409.  
  1410. return(result);                    /* 0 if no error, else errorcode */
  1411. }
  1412.  
  1413.                 Snippet 2
  1414.  
  1415.  
  1416.     Code snippet 2 sets up an arbitrary viewport size (I usually load a picture
  1417. into Deluxe Paint and write down the coordinates of where I want the 3D walls
  1418. to be), and then calls the engine to intialize. Upon return the value of result
  1419. will either be zero, meaning no problems, or one of the ERR_ codes listed in
  1420. the ACK3D.H header file. If an error occurs the application should NOT continue,
  1421. since unpredictable results WILL occur.
  1422.  
  1423.     At this point the engine has filled in quite a bit of the ACKENG structure
  1424. with information about the viewport and the map file that was read in and
  1425. processed. A buffer is automatically allocated for the ScreenBuffer pointer in
  1426. the structure and the map arrays now contain the necessary wall and object
  1427. information to build the POV. But, we can't do that just yet. First we have
  1428. to decide on a couple more things. Lets take code snippet 2 and expand it;
  1429.  
  1430.  
  1431. #include "ack3d.h"
  1432.  
  1433. #define VIEW_X        80        /* Size of the 3D viewport */
  1434. #define VIEW_X1        240
  1435. #define VIEW_Y        40
  1436. #define VIEW_Y1        160
  1437.  
  1438. #define CEILING_COLOR    23
  1439. #define FLOOR_COLOR    27
  1440.  
  1441. #define DOORSPEED    4        /* Doors need a speed to open/close */
  1442.  
  1443. #define PLAYER_X    390        /* Initial X coordinate */
  1444. #define PLAYER_Y    260        /* Initial Y coordinate */
  1445. #define PLAYER_ANGLE    480        /* Initial POV angle    */
  1446.  
  1447.  
  1448.     char        *MapFileName = "LEVEL1.MAP";
  1449.  
  1450.  
  1451. int AppSetupEngine(void)
  1452. {
  1453.     int        result;
  1454.  
  1455. ae->WinStartX = VIEW_X;
  1456. ae->WinStartY = VIEW_Y;                /* Plug in the size we want */
  1457. ae->WinEndX   = VIEW_X1;            /* for our viewport        */
  1458. ae->WinEndY   = VIEW_Y1;
  1459.  
  1460.  
  1461. ae->xPlayer    = PLAYER_X;            /* Setup intial coordinates */
  1462. ae->yPlayer    = PLAYER_Y;            /* for the POV        */
  1463. ae->PlayerAngle = PLAYER_ANGLE;
  1464. ae->DoorSpeed    = DOORSPEED;            /* Set a default door speed */
  1465.  
  1466. result = AckInitialize(ae);            /* Then initialize the engine! */
  1467.  
  1468. if (result)
  1469.     return(result);                /* Error, so get out now    */
  1470.  
  1471. result = AckReadMapFile(ae,MapFileName);
  1472. if (result)
  1473.     return(result);
  1474.  
  1475. ae->TopColor    = CEILING_COLOR;        /* Setup our colors for the */
  1476. ae->BottomColor = FLOOR_COLOR;            /* background....        */
  1477.  
  1478. ae->LightFlag = SHADING_ON;            /* Yes, we want light shading */
  1479. result = AckBuildBackground(ae);        /* Build the ceiling, floor      */
  1480.  
  1481. if (result)
  1482.     return(result);                /* Error, so get out now    */
  1483.  
  1484. return(result);                    /* 0 if no error, else errorcode */
  1485. }
  1486.  
  1487.                 Snippet 3
  1488.  
  1489.  
  1490.  
  1491.     Code snippet 3 adds to snippet 2 by setting up the background ceiling and
  1492. floor that the ACK engine requires. In the example above we decided to shade
  1493. the background. By using SHADING_OFF (both found in ACK3D.H) we could also
  1494. decide to have a solid background. Note the two defines for ceiling and floor
  1495. color, these can be any color from 0 to 255, whatever is appropriate for the
  1496. applications.
  1497.  
  1498.     Also note that the initial coordinates and angle of the POV are setup here
  1499. but do not need to be. These can be deferred until the call to AckBuildView()
  1500. is made. They are done here merely for convienence.
  1501.  
  1502.  
  1503.     Okay, things are coming along nicely. Now we need to decide if an overlay
  1504. is needed or not. Remember an overlay is only need if there will be some part
  1505. of the full screen that will always display over the 3D walls. An example
  1506. would be gothic pillars, or perhaps magic staffs that border the full screen
  1507. display. Whatever the application needs to give the visual effect.
  1508.  
  1509.     An overlay is not mandatory and does induce a slight speed degradation when
  1510. displaying the engine. It also means the function AckDisplayScreen() must be
  1511. called to actually use the overlay (unless the application has it's own
  1512. routine to handle it).
  1513.  
  1514.     Let's say we want an overlay, this is what we would do;
  1515.  
  1516.  
  1517. #include "ack3d.h"
  1518.  
  1519.  
  1520.     char        *PictureFile = "MYPIC.LBM";
  1521.  
  1522.  
  1523. int AppSetupOverlay(void)
  1524. {
  1525.     int        result = 0;
  1526.     UCHAR   far *OverlayPic;
  1527.  
  1528. OverlayPic = AckReadiff(PictureFile);    /* Load a Deluxe Paint picture    */
  1529.  
  1530. if (OverlayPic == NULL)            /* Whoops, got a problem    */
  1531.     return(-1);                /* So return with an error    */
  1532.  
  1533.  
  1534. result = AckCreateOverlay(ae,&OverlayPic[4]);    /* Compile the overlay    */
  1535.  
  1536. free(OverlayPic);            /* Free up the picture unless    */
  1537.                     /* we want to use it later    */
  1538.  
  1539. return(result);
  1540. }
  1541.  
  1542.                 Snippet 4
  1543.  
  1544.  
  1545.     This example reads in a Deluxe Paint LBM file and calls the ACK engine to
  1546. compile it. The resulting overlay sequence will be in the interface structure
  1547. pointed to by OverlayBuffer (unless an error occurs).
  1548.  
  1549.     One change to this code snippet would be to keep the picture buffer around
  1550. so it can be displayed on the screen. Note, only the portion which will cover
  1551. the 3D viewport will be compiled, NOT the entire picture.
  1552.  
  1553.     The application can use the supplied LBM read routine if it wishes, or read
  1554. in the picture on its own, as long as the picture buffer passed to the routine
  1555. is a flat 320 by 200 (64000 byte) image that the overlay section can be snipped
  1556. out of.
  1557.  
  1558.     The AckReadiff() routine places the width and height of the image in the
  1559. first four bytes of the buffer, which is why  &OverlayPic[4] was passed to
  1560. the engine.
  1561.  
  1562.     Again remember, the overlay is optional and only needs to be used if part
  1563. of the screen is going to appear over the walls.
  1564.  
  1565.  
  1566.     By now we've setup quite a bit of the information we'll need to actually
  1567. draw a 3D view of our map, but things will look pretty bad if we try to draw
  1568. at this point, we don't have any bitmaps yet! Let's proceed with what is needed
  1569. to get some walls into our application;
  1570.  
  1571.  
  1572. #include "ack3d.h"
  1573.  
  1574.  
  1575. typedef struct {
  1576.     int    Number;
  1577.     int    Type;
  1578.     char    *Name;
  1579. } BMTABLE;
  1580.  
  1581.  
  1582.     BMTABLE bmTable[] = {
  1583.         1   ,TYPE_WALL        ,"swall1.bbm",
  1584.         2   ,TYPE_WALL        ,"swall2.bbm",
  1585.         3   ,TYPE_WALL        ,"swall3.bbm",
  1586.         4   ,TYPE_WALL        ,"swall4.bbm",
  1587.         5   ,TYPE_WALL        ,"swall5.bbm",
  1588.         58  ,TYPE_WALL        ,"secret.bbm",
  1589.         59  ,TYPE_WALL        ,"secret.bbm",
  1590.         60  ,TYPE_WALL        ,"sdoor.bbm",
  1591.         61  ,TYPE_WALL        ,"sside.bbm",
  1592.         62  ,TYPE_WALL        ,"sdoor.bbm",
  1593.         1   ,TYPE_OBJECT    ,"eyeball.bbm",
  1594.         2   ,TYPE_OBJECT    ,"treasure.bbm",
  1595.         -1  ,-1            ,""            /* End of table */
  1596.         };
  1597.  
  1598.  
  1599. int AppLoadBitmaps(void)
  1600. {
  1601.     int        result;
  1602.     int        i = 0;
  1603.  
  1604. while (bmTable[i].Number != -1)
  1605.     {
  1606.     result = AckLoadBitmap(ae,
  1607.                bmTable[i].Number,
  1608.                bmTable[i].Type,
  1609.                bmTable[i].Name);
  1610.  
  1611.     if (result)                    /* Error during load */
  1612.     break;                    /* so get out now    */
  1613.  
  1614.     i++;                    /* Next index in table */
  1615.     }
  1616.  
  1617.  
  1618. return(result);
  1619. }
  1620.  
  1621.                 Snippet 5
  1622.  
  1623.  
  1624.     What code snippet 5 does is loop through a table and load all the bitmaps
  1625. for this example application (the bitmap names are arbitrary). Should an error
  1626. occur the routine exits immediately and returns the error to the caller. Note
  1627. also that this same routine can be used to load objects as well as walls, just
  1628. use TYPE_OBJECT instead of TYPE_WALL (defined in ACK3D.H) for the objects.
  1629.  
  1630.     Upon return from code snippet 5 we'll have all the bitmaps loaded that are
  1631. needed to begin. If no objects are going to be used then we can proceed, but
  1632. for example purposes let's say we have the two objects loaded in snippet 5 to
  1633. setup. We do this by calling the function AckCreateObject() as in the following
  1634. example;
  1635.  
  1636.  
  1637. #include "ack3d.h"
  1638.  
  1639.  
  1640. int AppSetupObjects(void)
  1641. {
  1642.     int        result;
  1643.     UCHAR    BitmapNumbers[2];
  1644.  
  1645. ae->ObjList[1].Dir   = 0;    /* Direction doesn't matter */
  1646. ae->ObjList[1].Speed = 0;    /* is a stationary object   */
  1647.  
  1648. BitmapNumbers[0] = 1;        /* Bitmap to use with object (eyeball) */
  1649.  
  1650. result = AckCreateObject(ae,1,1,BitmapNumbers);
  1651.  
  1652. if (result)            /* An error occurred */
  1653.     return(result);        /* so get out now    */
  1654.  
  1655. ae->ObjList[2].Dir   = 0;    /* Again a direction is irrelavent */
  1656. ae->ObjList[2].Speed = 0;    /* Because speed 0 is stationary   */
  1657.  
  1658. BitmapNumbers[0] = 2;        /* Bitmap to use (treasure) */
  1659.  
  1660. result = AckCreateObject(ae,2,1,BitmapNumbers);
  1661.  
  1662. return(result);
  1663. }
  1664.  
  1665.                 Snippet 6
  1666.  
  1667.  
  1668.     Snippet 6 is a brute force method of creating the objects. A more elegant
  1669. method would be to setup a table to create the objects, similiar to what we
  1670. did in snippet 5 with the bitmaps.
  1671.  
  1672.  
  1673.  
  1674.     At this point we've got the engine initialized, a background buffer built,
  1675. bitmaps loaded, and objects created. We're not even in graphics mode yet! The
  1676. ACK engine provides some support functions that may be used (unless the
  1677. application has its own), to setup graphics mode and later go back to text
  1678. mode. These functions are;
  1679.  
  1680.  
  1681.     AckSetVGAmode();  <- Sets video into mode 13h (320x200 w/ 256 colors)
  1682.  
  1683.     AckSetTextmode(); <- Sets video into mode 3 (80x25 16 color text)
  1684.  
  1685.  
  1686.     Use these if desired, they are just thrown in as support routines. Another
  1687. routine can also be used, this one reads in a palette file and sets up the
  1688. VGA palette registers;
  1689.  
  1690.  
  1691.     AckLoadAndSetPalette( FileName );  <- Reads a palette file and sets regs.
  1692.  
  1693.  
  1694.     This routine may be used AFTER the video is placed in graphics mode. Pass
  1695. the name of a 768 byte palette file to use. Upon return the new palette will
  1696. be set. Let's put these routines into a code snippet;
  1697.  
  1698.  
  1699. #include "ack3d.h"
  1700.  
  1701.  
  1702.     char        *PalFile = "DEMO.PAL";
  1703.  
  1704.  
  1705.  
  1706. int AppSetGraphics(void)
  1707. {
  1708.     int        result;
  1709.  
  1710.  
  1711. AckSetVGAmode();    /* Go into graphics */
  1712.  
  1713. result = AckLoadAndSetPalette(PalFile);
  1714.  
  1715. return(result);
  1716. }
  1717.  
  1718.  
  1719.                 Snippet 7
  1720.  
  1721.  
  1722.  
  1723.  
  1724.     Okay, we've got the beginnings of a 3D game! As part of this introduction
  1725. we need to be concerned with only a couple more things. Beyond that it becomes
  1726. the applications responsibility to handle user interaction with the engine.
  1727.  
  1728.     Once we're ready to begin displaying the POV on the screen we need to make
  1729. one more mandatory call to the engine to tell it to build the current scene. The
  1730. code snippet below shows this process (for now we'll assume the application is
  1731. going to let the ACK engine perform the actual display);
  1732.  
  1733.  
  1734.  
  1735. #include "ack3d.h"
  1736.  
  1737.  
  1738. void AppShow3D(void)
  1739. {
  1740.  
  1741. /* Any preprocessing the application wishes to do can go here */
  1742.  
  1743. AckBuildView(ae);    /* Tell the ACK engine to construct the POV */
  1744.  
  1745. AckDisplayScreen(ae);    /* Display the POV on the video screen */
  1746.  
  1747.  
  1748. }
  1749.  
  1750.                 Snippet 8
  1751.  
  1752.  
  1753.  
  1754.     Remember that the initial coordinates of the POV were setup in code snippet
  1755. number 3 above. They can easily be setup anytime before calling AckBuildView()
  1756. if the application so decides.
  1757.  
  1758.  
  1759.  
  1760.  
  1761.     It's high time we put all these code snippets together into a running
  1762. example program. This is shown below;
  1763.  
  1764.  
  1765.  
  1766. #include "ack3d.h"
  1767.  
  1768. #define VIEW_X        80        /* Size of the 3D viewport */
  1769. #define VIEW_X1        240
  1770. #define VIEW_Y        40
  1771. #define VIEW_Y1        160
  1772.  
  1773. #define CEILING_COLOR    23
  1774. #define FLOOR_COLOR    27
  1775.  
  1776.  
  1777. #define PLAYER_X    390        /* Initial X coordinate */
  1778. #define PLAYER_Y    260        /* Initial Y coordinate */
  1779. #define PLAYER_ANGLE    480        /* Initial POV angle    */
  1780.  
  1781.  
  1782.     char        *MapFileName = "LEVEL1.MAP";
  1783.     char        *PictureFile = "MYPIC.LBM";
  1784.     char        *PalFile     = "DEMO.PAL";
  1785.  
  1786. typedef struct {
  1787.     int    Number;
  1788.     int    Type;
  1789.     char    *Name;
  1790. } BMTABLE;
  1791.  
  1792.  
  1793.     BMTABLE bmTable[] = {
  1794.         1   ,TYPE_WALL        ,"swall1.bbm",
  1795.         2   ,TYPE_WALL        ,"swall2.bbm",
  1796.         3   ,TYPE_WALL        ,"swall3.bbm",
  1797.         4   ,TYPE_WALL        ,"swall4.bbm",
  1798.         5   ,TYPE_WALL        ,"swall5.bbm",
  1799.         58  ,TYPE_WALL        ,"secret.bbm",
  1800.         59  ,TYPE_WALL        ,"secret.bbm",
  1801.         60  ,TYPE_WALL        ,"sdoor.bbm",
  1802.         61  ,TYPE_WALL        ,"sside.bbm",
  1803.         62  ,TYPE_WALL        ,"sdoor.bbm",
  1804.         1   ,TYPE_OBJECT    ,"eyeball.bbm",
  1805.         2   ,TYPE_OBJECT    ,"treasure.bbm",
  1806.         -1  ,-1            ,""            /* End of table */
  1807.         };
  1808.  
  1809.  
  1810. /* Prototypes */
  1811. int AppInitialize(void);
  1812. int AppSetupEngine(void);
  1813. int AppSetupOverlay(void);
  1814. int AppLoadBitmaps(void);
  1815. int AppSetupObjects(void);
  1816. int AppSetGraphics(void);
  1817. void AppShow3D(void);
  1818.  
  1819.  
  1820. /* Entry point for application */
  1821.  
  1822. int main(void)
  1823. {
  1824.     int        result,done = 0;
  1825.  
  1826. result = AppInitialize();
  1827.  
  1828. if (result)
  1829.     {
  1830.     printf("Error initializing: ErrorCode = %d\n",result);
  1831.     return(1);
  1832.     }
  1833.  
  1834.  
  1835. result = AppSetupEngine();
  1836.  
  1837. if (result)
  1838.     {
  1839.     printf("Error setting up ACK engine: ErrorCode = %d\n",result);
  1840.     return(1);
  1841.     }
  1842.  
  1843.  
  1844. result = AppSetupOverlay();
  1845.  
  1846. if (result)
  1847.     {
  1848.     printf("Error loading overlay: ErrorCode = %d\n",result);
  1849.     return(1);
  1850.     }
  1851.  
  1852.  
  1853. result = AppLoadBitmaps();
  1854.  
  1855. if (result)
  1856.     {
  1857.     printf("Error loading bitmaps: ErrorCode = %d\n",result);
  1858.     return(1);
  1859.     }
  1860.  
  1861.  
  1862. result = AppSetupObjects();
  1863.  
  1864. if (result)
  1865.     {
  1866.     printf("Error creating objects: ErrorCode = %d\n",result);
  1867.     return(1);
  1868.     }
  1869.  
  1870.  
  1871.  
  1872. result = AppSetGraphics();
  1873.  
  1874. if (result)
  1875.     {
  1876.     AckSetTextmode();
  1877.     printf("Error loading palette: ErrorCode = %d\n",result);
  1878.     return(1);
  1879.     }
  1880.  
  1881.  
  1882.  
  1883. while (!done)
  1884.     {
  1885.     AppShow3D();
  1886.  
  1887.  
  1888.     if (getch() == 27)    /* Check for the escape key */
  1889.     break;
  1890.  
  1891.     }
  1892.  
  1893.  
  1894.  
  1895. AckSetTextmode();
  1896.  
  1897. return(0);
  1898. }
  1899.  
  1900.  
  1901.                 Example 3
  1902.  
  1903.  
  1904.  
  1905.  
  1906. (20) Closing comments:
  1907.  
  1908.     And here I said at the start of this document that writing docs is too
  1909. much like work! Anyway, I hope the information provided above, as well as the
  1910. actual source to the ACK engine, is enough to get you started on your own
  1911. 3D adventure. It's been a very exciting project and I've met alot of nice folks
  1912. who have pitched in and helped in many ways. I wish to express thanks to all
  1913. of you who made this possible and advanced thanks to all of you who may use the
  1914. engine to produce more games for all of us to enjoy.
  1915.  
  1916.     Those who have been of GREAT help are;
  1917.  
  1918.     Jaimi McEntire who helped a great deal with programming and graphics.
  1919.         Jaimi also did the neat space station picture in the title!
  1920.  
  1921.     Ken Lemieux who provided the space dudes and shuttle!
  1922.  
  1923.     Steve Salter who did ALOT of the wall and object graphics!
  1924.  
  1925.     Frank Sachse who provided the sound routine interface and music!
  1926.  
  1927.     Ron Sachse who provided some of the wall bitmaps!
  1928.  
  1929.     Mark Betz who allowed me to use his fading routines!
  1930.  
  1931.     Bart Stewart who built an image editor for this thing!
  1932.  
  1933.     Michael Wilson who's XMS code was readily available and is working
  1934.         great!
  1935.  
  1936.     Thanks guys! Without you the ACK engine and demo would not have been
  1937. possible!
  1938.  
  1939.     Now some bad news: There is currently a problem with displaying objects that
  1940. sometimes causes "ghosts" images to be displayed. While this does not cause any
  1941. damage to anything it is very annoying and I haven't been able to track down the
  1942. cause. If anyone has any suggestions or ideas I'd appreciate hearing them and
  1943. building an update for others. Thanks!
  1944.  
  1945.     One last note: This will be my final installment of ACK3D for awhile. I'm
  1946. not going to drop out of the picture totally but I've got some new things that
  1947. I'm eager to try out! Maybe some more goodies will come out of it, one can
  1948. never tell.
  1949.  
  1950.     If you wish to reach me on CompuServe I'm usually hanging around the Game
  1951. Design library of The Gamers forum, or you can email me direct. My CompuServe
  1952. account is 72355,655.
  1953.  
  1954.     Sincerely,
  1955.     Lary Myers
  1956.  
  1957.  
  1958.